package com.pwrd.sframe.netty;

import com.pwrd.sframe.support.exception.SysException;
import com.pwrd.sframe.core.Node;
import com.pwrd.sframe.support.constant.ConfigConst;
import com.pwrd.sframe.support.log.Log;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;

/**
 * @Auther syc.
 * @Date 2016/12/29 18:18.
 */
public class ConnServer extends Thread {

    public static Node CONN_NODE;

    private int maxMsgLength = 100 * 1024 * 1024; //每帧数据最大长度，bit为单位
    private int lengthStartLocation = 0; //长度字节的开始位置
    private int lengthLength = 4; //表示长度的字节数，以字节为单位
    private int withOutHeaderLengthAdjust = -4; //长度包含表示长度的字节，0则表示长度不包含长度的字节
    private int ignoreLength = 4; //需要忽略的信息长度，从头开始计算，例如需要删除头部表示长度的信息

    public static void startUp(Node node) {
        // 设置处理消息的Node
        CONN_NODE = node;
        // 启动连接请求接受线程
        ConnServer server = new ConnServer();
        server.setName("Connection Server");
        server.start();
    }

    @Override
    public void run() {
        EventLoopGroup boss = new NioEventLoopGroup(); //默认值为1

        EventLoopGroup workers = new NioEventLoopGroup(ConfigConst.CONNSERVER_THREAD_NUM); // 默认值为2*cpu核数

        ServerBootstrap boot = new ServerBootstrap();

        try {
            boot.group(boss, workers)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, ConfigConst.CONNSERVER_BACKLOG_NUM)
                    .childOption(ChannelOption.SO_KEEPALIVE, true) //保持长连接状态
                    .childOption(ChannelOption.TCP_NODELAY, true)    //禁用Nagle,使消息立即发出去，不用等待到一定的数据量才进行发送
                    .childHandler(new ChannelInitializer() {
                        @Override
                        protected void initChannel(Channel ch) throws Exception {
                            ChannelPipeline p = ch.pipeline();
                            p.addLast(new LengthFieldBasedFrameDecoder(maxMsgLength, lengthStartLocation, lengthLength, withOutHeaderLengthAdjust ,ignoreLength));
                            p.addLast(new ServerHandler());
                        }
                    });

            Collection<Channel> channels = new ArrayList<>(ConfigConst.CONNSERVER_PORTS.length);

            ChannelFuture future = null;
            // Bind multi ports to one boss
            for (int port : ConfigConst.CONNSERVER_PORTS) {
                future = boot.bind(port);
            }

            Log.conn.info("Startup the connect server with port = {}", Arrays.toString(ConfigConst.CONNSERVER_PORTS));

            // Start to accept incoming connections.
            ChannelFuture futureNext = future.sync();

            // Wait until the server socket is closed.
            futureNext.channel().closeFuture().sync();

        } catch (Exception e) {
            throw new SysException(e, "Error caused by startup connect server");
        } finally {
            // Shut down all event loops to terminate all threads.
            boss.shutdownGracefully();
            workers.shutdownGracefully();
        }
    }
}
